<?php
/*-------------------------------------------------------------------------\
|
|  ========================================================
|  Fusion Registry GPL
|  Copyright (C) 2007 Fusion Scripts
|
|  This program is free software; you can redistribute it and/or
|  modify it under the terms of the GNU General Public License
|  as published by the Free Software Foundation; either version 2
|  of the License, or (at your option) any later version.
|
|  This program is distributed in the hope that it will be useful,
|  but WITHOUT ANY WARRANTY; without even the implied warranty of
|  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
|  GNU General Public License for more details.
|
|  You should have received a copy of the GNU General Public License
|  along with this program; if not, write to the Free Software
|  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
|  ========================================================
|
|  Email: info@fusionscripts.com
|  Web-Site: http://www.fusionscripts.com/
|
|  $Id: ratings.php 4 2008-08-03 01:43:03Z registry.fusion $
|
\-------------------------------------------------------------------------*/

/**
 * Rating functions for the Fusion Registry.
 *
 * @package Fusion Registry
 * @subpackage lib
 * @version $Id: ratings.php 4 2008-08-03 01:43:03Z registry.fusion $
 */

if ( ! defined( 'IN_IPB' ) )
{
    print "<h1>Incorrect access</h1>You cannot access this file directly. If you have recently upgraded, make sure you upgraded all the relevant files.";
    exit();
}


/**
 * Class 'registry_ratings' contains rating functions for the Fusion Registry.
 *
 * @package Fusion Registry
 * @subpackage lib
 */
class registry_ratings
{
    var $ipsclass;

    /** @var object */
    var $class  = "";

    /**
     * Constructor, nothing too special here
     *
     * @param object $class Our parent class object
     * @return boolean TRUE
     */
    function auto_run($class)
    {
        $this->class = &$class;

        return TRUE;
    }


    /**
     * Get Rating definitions
     *
     * This function's job is only to retrieve the data from the database,
     * it should not process the returned data.
     *
     * @param array $opts Optional custom SQL cmd/value pair attributes
     * @return array An array of an array, outer key is field_id
     */
    function get_rating_defs($opts=array())
    {
        $query = "SELECT rating_id,rating_name,rating_order FROM ibf_registry_rating_defs";

        // Did we get any restrictions passed?
        if (count($opts) > 0)
        {
            foreach ($opts as $cmd => $val)
            {
                $query .= " $cmd $val";
            }
        }

        $rs = $this->ipsclass->DB->query($query);

        // Build our data structure to hold the custom fields
        $rating_defs = array();

        while ( $row = $this->ipsclass->DB->fetch_row($rs) )
        {
            $rating_defs[ $row['rating_id'] ] = $row;
        }

        return $rating_defs;
    }


    /**
     * Figures out the html for the rating value smartly
     *
     * @param decimal $rating The raw rating decimal number
     * @return string Raw html for viewing the rating with images
     */
    function get_rating_image($rating)
    {
        $total_stars = $this->class->registry_config['rating_max'] - $this->class->registry_config['rating_min'];

        // If they aren't using '0' as min take that into account here
        $weighted_rating = $rating - $this->class->registry_config['rating_min'];

        // Calculate the # of full stars to use
        $full_stars = floor($weighted_rating);

        // Do we need a half star?
        $half_star = is_float($weighted_rating) ? 1 : 0;

        // And how many left over empty ones...
        $empty_stars = $total_stars - $full_stars - $half_star;

        // Construct the html
        $image_html = '';

        for ($x=1; $x <= $full_stars; $x++)
        {
            $image_html .= '<{R_STAR_100}>';
        }

        if ( $half_star == 1 )
        {
            $image_html .= '<{R_STAR_50}>';
        }

        for ($x=1; $x <= $empty_stars; $x++)
        {
            $image_html .= '<{R_STAR_0}>';
        }

        return $image_html;
    }


    /**
     * Constucts the rating input html select field
     *
     * @param array $opts With key 'rating_row' as returned from get_rating_defs
     * @return string Raw HTML select field
     */
    function build_rating_select($opts=array())
    {
        // If we don't have a rating_row def return empty handed
        if ( empty($opts['rating_row']) == TRUE )
        {
            return;
        }

        // Determine neutral rating
        $mid_rating = round( ($this->class->registry_config['rating_max'] + $this->class->registry_config['rating_min'])/2 );

        // Begin html
        $html = "<select name='". $opts['rating_row']['rating_id'] ."'>";

        // Create options
        for ($x = $this->class->registry_config['rating_min']; $x <= $this->class->registry_config['rating_max']; $x++ )
        {
            if ( $x == $mid_rating )
            {
                $selected = "selected='selected'";
            }
            else
            {
                $selected = '';
            }

            $html .= "<option value='$x' $selected>$x</option>";
        }

        // End html
        $html .= '</select>';

        return $html;
    }


    /**
     * Get Rating Report for an item
     *
     * This function is the access point when viewing an item for retrieving
     * the item's current overall ratings. It does not/should not access individual
     * member ratings as that would slow down access.
     *
     * @param integer $item_id
     * @return array An array of arrays, outer key is rating_id, inner array has keys rating_count, rating_sum
     */
    function get_rating_report($item_id)
    {
        $report = array();

        // First get the current list of rating defs
        $ratings_defs = $this->get_rating_defs( array( 'ORDER BY' => 'rating_order ASC' ) );

        // Then get report for each of the rating defs for this item
        foreach ( $ratings_defs as $rating_id => $rating_row )
        {
            // Initialize the report for this rating
            $report[$rating_id] = array();

            $query = "SELECT rating_count, rating_sum FROM ibf_registry_rating_reports ".
                     "WHERE item_id = '$item_id' AND rating_id = '$rating_id'";

            $rs = $this->ipsclass->DB->query($query);

            // Add to report to be returned
            if ( $this->ipsclass->DB->get_num_rows() )
            {
                $report[$rating_id] = $this->ipsclass->DB->fetch_row($rs);
            }

            // While we are at it, let's generate the html here also.
            if ( $report[$rating_id]['rating_count'] > 0 )
            {
                // If there are votes, report real value
                $report[$rating_id]['rating_image'] = $this->get_rating_image( $report[$rating_id]['rating_sum'] / $report[$rating_id]['rating_count'] );
            }
            else
            {
                // If there aren't any votes report medium neutral value
                $report[$rating_id]['rating_image'] = $this->get_rating_image( ($this->class->registry_config['rating_max'] + $this->class->registry_config['rating_min'])/2 );
            }

            // Also cram in rating info
            $report[$rating_id]['rating_name'] = $rating_row['rating_name'];
            $report[$rating_id]['rating_order'] = $rating_row['rating_order'];
        }

        return $report;
    }


    /**
     * Record a rating submission from an end user.
     *
     * This function's job is to determine if they have voted previously or if this
     * is a new submission. Then it needs to update the ratings report for the item.
     * By maintaining the ratings report here we will cut down on a lot of overhead
     * while viewing an item and retrieving the current ratings.
     *
     * @param integer $item_id
     * @param array $user_ratings Each rating id and the submitted rating by the user
     * @return boolean TRUE
     */
    function record_rating($item_id, $user_ratings=array())
    {
        // Check for valid item_id
        $this->ipsclass->DB->query("SELECT id FROM ibf_registry_items WHERE id = '$item_id'");
        if ( !$this->ipsclass->DB->get_num_rows() )
        {
            $this->ipsclass->Error( array( LEVEL => 1, MSG => 'is_broken_link') );
        }

        // Will be used to store the created and updated time in DB.
        // We are not using mysql's timestamp format because we are
        // control freaks :)
        $epoch_time = time();

        // First get the current list of ratings defs, we only want to record valid
        // rating fields.
        $ratings_def = $this->get_rating_defs();

        // Calculate default rating if no rating submitted by user, this should
        // never really get used, but it's here just incase.
        $rating_default = round( ($this->class->registry_config['rating_min'] + $this->class->registry_config['rating_min'])/2 );

        // Loop through each valid rating def and determine if they have voted before or not
        foreach ( $ratings_def as $rating_id => $rating_row )
        {
            // Determine rating value to be recorded
            $rating_value = ( !empty( $user_ratings[$rating_id] ) ) ? $user_ratings[$rating_id] : $rating_default;

            // Have they voted for this before?
            $query = "SELECT entry_id,rating_value FROM ibf_registry_rating_entries ".
                     "WHERE item_id = '$item_id' AND rating_id = '$rating_id' AND member_id = '". $this->ipsclass->member['id'] ."'";

            $rs = $this->ipsclass->DB->query($query);

            if ( !$this->ipsclass->DB->get_num_rows() )
            {
                // They have not voted for this before, add new entry
                $db_string = $this->ipsclass->DB->compile_db_insert_string( array( 'member_id' => $this->ipsclass->member['id'],
                                                                                   'item_id' => $item_id,
                                                                                   'rating_id' => $rating_id,
                                                                                   'rating_value' => $rating_value,
                                                                                   'rating_date' => $epoch_time,
                                                                          )      );

                $this->ipsclass->DB->query("INSERT INTO ibf_registry_rating_entries (" .$db_string['FIELD_NAMES']. ") VALUES (". $db_string['FIELD_VALUES'] .")");

                // Now we need to add this to the rating report. First see if a report exists,
                // if not create one.
                $rs2 = $this->ipsclass->DB->query("SELECT entry_id, rating_count, rating_sum FROM ibf_registry_rating_reports ".
                                                  "WHERE item_id = '$item_id' AND rating_id = '$rating_id'");

                if ( !$this->ipsclass->DB->get_num_rows() )
                {
                    // No report yet, create new one
                    $db_string = $this->ipsclass->DB->compile_db_insert_string( array( 'item_id' => $item_id,
                                                                                       'rating_id' => $rating_id,
                                                                                       'rating_count' => 1,
                                                                                       'rating_sum' => $rating_value,
                                                                              )      );

                    $this->ipsclass->DB->query("INSERT INTO ibf_registry_rating_reports (" .$db_string['FIELD_NAMES']. ") VALUES (". $db_string['FIELD_VALUES'] .")");
                }
                else
                {
                    // Report exists, update it
                    $existing_report = $this->ipsclass->DB->fetch_row($rs2);

                    $db_string = $this->ipsclass->DB->compile_db_update_string( array( 'rating_count' => ++$existing_report['rating_count'],
                                                                                       'rating_sum' => $existing_report['rating_sum'] + $rating_value,
                                                                              )      );

                    $this->ipsclass->DB->query("UPDATE ibf_registry_rating_reports SET $db_string WHERE entry_id = '". $existing_report['entry_id'] ."'");
                }
            }
            else
            {
                // They have voted before, update existing vote
                $existing = $this->ipsclass->DB->fetch_row($rs);

                $epoch_time = time();

                $db_string = $this->ipsclass->DB->compile_db_update_string( array( 'rating_value' => $rating_value,
                                                                                   'rating_date' => $epoch_time,
                                                                          )      );

                $this->ipsclass->DB->query("UPDATE ibf_registry_rating_entries SET $db_string WHERE entry_id = '". $existing['entry_id'] ."'");

                // Now we need to update the rating report smartly (increase/decrease accordingly)
                // There really should already be a report, but if something is really wrong
                // we'll create a new one here just incase. This will patch a problem if it exists
                // but a clean up routine will need to be run to fix this items rating report.
                $rs2 = $this->ipsclass->DB->query("SELECT entry_id, rating_count, rating_sum FROM ibf_registry_rating_reports ".
                                                  "WHERE item_id = '$item_id' AND rating_id = '$rating_id'");

                if ( !$this->ipsclass->DB->get_num_rows() )
                {
                    // No report yet, create new one
                    $db_string = $this->ipsclass->DB->compile_db_insert_string( array( 'item_id' => $item_id,
                                                                                       'rating_id' => $rating_id,
                                                                                       'rating_count' => 1,
                                                                                       'rating_sum' => $rating_value,
                                                                              )      );

                    $this->ipsclass->DB->query("INSERT INTO ibf_registry_rating_reports (" .$db_string['FIELD_NAMES']. ") VALUES (". $db_string['FIELD_VALUES'] .")");
                }
                else
                {
                    // Report exists, update it
                    $existing_report = $this->ipsclass->DB->fetch_row($rs2);

                    $db_string = $this->ipsclass->DB->compile_db_update_string( array( 'rating_sum' => ( $existing_report['rating_sum'] - $existing['rating_value'] ) + $rating_value ) );

                    $this->ipsclass->DB->query("UPDATE ibf_registry_rating_reports SET $db_string WHERE entry_id = '". $existing_report['entry_id'] ."'");
                }
            }
        }

        return TRUE;
    }
}

?>